package cn.xzc.job.cocurrent;

import cn.xzc.job.ErrorHandler;
import cn.xzc.job.Trigger;
import cn.xzc.job.pojo.DelegatingErrorHandlingRunnable;
import cn.xzc.job.type.SimpleTimerContext;

import java.time.Clock;
import java.util.Date;
import java.util.concurrent.*;

/**
 * 根据触发器执行定时任务
 *
 * @author xzc
 * 当前时间 2024-02-12 21:30:00
 */

class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements ScheduledFuture<Object> {

    /**
     * 触发器
     */
    private final Trigger trigger;

    /**
     * 当前任务执行的时间信息
     */
    private final SimpleTimerContext triggerContext;

    /**
     * 定时任务线程池
     */
    private final ScheduledExecutorService executor;
    /**
     * 作为使用synchronized的锁
     */
    private final Object triggerContextMonitor = new Object();
    /**
     * 线程回调通知
     */
    
    private ScheduledFuture<?> currentFuture;
    /**
     * 下一次执行的时间
     */
    
    private Date scheduledExecutionTime;


    public ReschedulingRunnable(Runnable delegate, Trigger trigger, Clock clock,
                                ScheduledExecutorService executor, ErrorHandler errorHandler) {

        super(delegate, errorHandler);
        this.trigger = trigger;
        this.triggerContext = new SimpleTimerContext(clock);
        this.executor = executor;
    }


    
    public ScheduledFuture<?> schedule() {
        synchronized (this.triggerContextMonitor) {
            this.scheduledExecutionTime = this.trigger.nextExecutionTime(this.triggerContext);
            if (this.scheduledExecutionTime == null) {
                return null;
            }
            long initialDelay = this.scheduledExecutionTime.getTime() - this.triggerContext.getClock().millis();
            this.currentFuture = this.executor.schedule(this, initialDelay, TimeUnit.MILLISECONDS);
            return this;
        }
    }

    private ScheduledFuture<?> obtainCurrentFuture() {
        return this.currentFuture;
    }

    @Override
    public void run() {
        Date actualExecutionTime = new Date(this.triggerContext.getClock().millis());
        super.run();
        Date completionTime = new Date(this.triggerContext.getClock().millis());
        synchronized (this.triggerContextMonitor) {
            this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
            if (!obtainCurrentFuture().isCancelled()) {
                schedule();
            }
        }
    }


    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        synchronized (this.triggerContextMonitor) {
            return obtainCurrentFuture().cancel(mayInterruptIfRunning);
        }
    }

    @Override
    public boolean isCancelled() {
        synchronized (this.triggerContextMonitor) {
            return obtainCurrentFuture().isCancelled();
        }
    }

    @Override
    public boolean isDone() {
        synchronized (this.triggerContextMonitor) {
            return obtainCurrentFuture().isDone();
        }
    }

    @Override
    public Object get() throws InterruptedException, ExecutionException {
        ScheduledFuture<?> curr;
        synchronized (this.triggerContextMonitor) {
            curr = obtainCurrentFuture();
        }
        return curr.get();
    }

    @Override
    public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        ScheduledFuture<?> curr;
        synchronized (this.triggerContextMonitor) {
            curr = obtainCurrentFuture();
        }
        return curr.get(timeout, unit);
    }

    @Override
    public long getDelay(TimeUnit unit) {
        ScheduledFuture<?> curr;
        synchronized (this.triggerContextMonitor) {
            curr = obtainCurrentFuture();
        }
        return curr.getDelay(unit);
    }

    @Override
    public int compareTo(Delayed other) {
        if (this == other) {
            return 0;
        }
        long diff = getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS);
        return (diff == 0 ? 0 : ((diff < 0) ? -1 : 1));
    }

}
